
package jp.moisture.othello.board;

import java.util.List;

import jp.moisture.othello.board.Board.State;
import jp.moisture.othello.player.AlphaBetaPlayer;
import jp.moisture.othello.player.Player;
import jp.moisture.othello.player.SimplePlayer;

public class Game {

    @SuppressWarnings("serial")
    private static class GameException extends Exception {
        private Player mPlayer;

        public GameException(Player player) {
            super(player + " give up.");
            mPlayer = player;
        }

        public Player getPlayer() {
            return mPlayer;
        }

    }

    public interface OnGameListener {
        public void onStart(Board board);

        public void onComplete(Board board);

        public boolean onChangeTurn(Board board, Player player);

        public void onPutStone(Player player, Board board, Cell cell);

        public void onGiveUp(Player player);
    }

    public static class PrintListener implements OnGameListener {
        long mStart;
        long mTime;

        public void onStart(Board board) {
            mStart = System.currentTimeMillis();
            mTime = mStart;
            System.out.println("start");
            System.out.println(board.toString());
        }

        public void onComplete(Board board) {
            System.out.println("complete");
            System.out.println(board.toString());
            System.out.println("time : " + (System.currentTimeMillis() - mStart));
        }

        public boolean onChangeTurn(Board board, Player player) {
            return true;
        }

        public void onPutStone(Player player, Board board, Cell cell) {
            long now = System.currentTimeMillis();
            long time = now - mTime;
            System.out.println(player + ":" + (cell == null ? "passed" : cell.toStringData()) + " Mind:" + player.hearMind() + " : " + time);
            if (cell != null) {
                System.out.println(board.toString());
            }
            mTime = now;
        }

        public void onGiveUp(Player player) {
            System.out.println(player + " give up.");
        }
    }

    protected Board mBoard;
    private Player[] mPlayers = new Player[2];
    private OnGameListener mListener;

    public Game(int size, Player player1, Player player2) {
        this(size, player1, player2, null);
    }

    public Game(int size, Player player1, Player player2, Board board) {
        if (board == null) {
            mBoard = new Board(size);
        } else {
            mBoard = board;
        }
        mPlayers[0] = player1;
        mPlayers[0].setColor(Stone.BLACK);
        mPlayers[1] = player2;
        mPlayers[1].setColor(Stone.WHITE);
        init();
    }

    private void init() {
        for (Player player : mPlayers) {
            player.setBoard(mBoard);
        }
    }

    public void setListener(OnGameListener listener) {
        mListener = listener;
    }

    public void start() {
        try {
            play();
        } catch (GameException e) {
            if (mListener != null) {
                mListener.onGiveUp(e.getPlayer());
            }
        }
    }

    public void play() throws GameException {
        if (mListener != null) {
            mListener.onStart(mBoard);
        }
        boolean prePass = false;
        while (true) {
            if (isOver()) {
                break;
            }

            Player player = mPlayers[mBoard.getTurnCount() % 2];
            if (mListener != null) {
                if (!mListener.onChangeTurn(mBoard, player)) {
                    throw new GameException(player);
                }
            }
            boolean pass = playNext(player);

            if (pass && prePass) {
                break;
            }
            prePass = pass;
        }
        mBoard.mCompleted = true;
        if (mListener != null) {
            mListener.onComplete(mBoard);
        }
    }

    private boolean mPrePass;

    public void start2() {
        if (mListener != null) {
            mListener.onStart(mBoard);
        }
        play2();
    }

    public void play2() {
        if (isOver()) {
            onGameComplete();
            return;
        }
        Player player = mPlayers[mBoard.getTurnCount() % 2];
        if (mListener != null) {
            if (!mListener.onChangeTurn(mBoard, player)) {
                return;
            }
        }
        playNext2(player);
    }

    public interface NextListener {
        boolean notifyNext(Cell cell);
    }

    public void playNext2(final Player player) {
        player.next(new NextListener() {

            @Override
            public boolean notifyNext(Cell cell) {

                if (cell == Cell.GIVE_UP) {
                    onGameComplete();
                    return true;
                }

                if (cell != null) {
                    List<Cell> cells = mBoard.findPutableCells(player.getColor());
                    if (!cells.contains(cell)) {
                        return false;
                    }
                }

                mBoard.putStone(cell, player.getColor());
                onGamePutStone(player, cell);

                return true;
            }
        });
        // try {
        // thread.join();
        // } catch (InterruptedException e) {
        // // TODO 自動生成された catch ブロック
        // e.printStackTrace();
        // }
    }

    public boolean playNext(Player player) throws GameException {
        Cell cell = player.next();
        if (cell == Cell.GIVE_UP) {
            throw new GameException(player);
        }

        mBoard.putStone(cell, player.getColor());
        if (mListener != null) {
            mListener.onPutStone(player, mBoard, cell);
        }

        if (cell == null) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isOver() {
        return mBoard.isFull();
    }

    public int getTurnCount() {
        return mBoard.getTurnCount();
    }

    public State getState() {
        return mBoard.getState();
    }

    public void onGameStart() {
        if (mListener != null) {
            mListener.onStart(mBoard);
        }
    }

    public void onGameComplete() {
        mBoard.mCompleted = true;
        if (mListener != null) {
            mListener.onComplete(mBoard);
        }
    }

    public void onGamePutStone(Player player, Cell cell) {
        boolean pass = cell == null;

        if (cell == Cell.GIVE_UP) {
            onGameGiveUp(player);
            return;
        }

        if (pass && mPrePass) {
            onGameComplete();
            return;
        }
        mPrePass = pass;
        if (mListener != null) {
            mListener.onPutStone(player, mBoard, cell);
        }

        play2();
    }

    public void onGameGiveUp(Player player) {
        if (mListener != null) {
            mListener.onGiveUp(player);
        }
    }

    public static void main(String[] args) {
//        Player player1 = new SimplePlayer("one");
        Player player1 = new AlphaBetaPlayer("one");

        Player player2 = new SimplePlayer("two");

        Game game = new Game(8, player1, player2);
        game.setListener(new Game.PrintListener());
        game.start2();
    }

}
